import 'package:jaspr/dom.dart';
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_riverpod/jaspr_riverpod.dart';
import 'package:jaspr_riverpod/legacy.dart';
import 'package:jaspr_test/jaspr_test.dart';

import '../utils.dart';

final counter = StateProvider((ref) => 0);
final autoDisposeCounter = StateProvider.autoDispose((ref) => 0);

void main() {
  group('context.watch', () {
    testComponents('returns provider state and rebuilds on change', (tester) async {
      tester.pumpComponent(
        providerApp((context) {
          return Button(
            label: '${context.watch(counter)}',
            onPressed: () {
              context.read(counter.notifier).state++;
            },
          );
        }),
      );

      expect(find.text('0'), findsOneComponent);

      await tester.click(find.tag('button'));
      await tester.pump();

      expect(find.text('1'), findsOneComponent);
    });

    testComponents('returns overridden provider state', (tester) async {
      tester.pumpComponent(
        providerApp((context) {
          return div([
            Builder(
              builder: (context) {
                return Button(
                  key: const ValueKey('a'),
                  label: 'a ${context.watch(counter)}',
                  onPressed: () {
                    context.read(counter.notifier).state++;
                  },
                );
              },
            ),
            ProviderScope(
              overrides: [counter.overrideWith((ref) => 10)],
              child: Builder(
                builder: (context) {
                  return Button(
                    key: const ValueKey('b'),
                    label: 'b ${context.watch(counter)}',
                    onPressed: () {
                      context.read(counter.notifier).state++;
                    },
                  );
                },
              ),
            ),
          ]);
        }),
      );

      expect(find.text('a 0'), findsOneComponent);
      expect(find.text('b 10'), findsOneComponent);

      await tester.click(find.byKey(const ValueKey('a')));
      await tester.pump();

      expect(find.text('a 1'), findsOneComponent);
      expect(find.text('b 10'), findsOneComponent);

      await tester.click(find.byKey(const ValueKey('b')));
      await tester.pump();

      expect(find.text('a 1'), findsOneComponent);
      expect(find.text('b 11'), findsOneComponent);
    });

    testComponents('provider is autodisposed when no longer watched', (tester) async {
      tester.pumpComponent(
        providerApp((context) {
          var showCounter = true;
          return StatefulBuilder(
            builder: (context, setState) {
              return div([
                Button(
                  label: showCounter ? '${context.watch(autoDisposeCounter)}' : 'hidden',
                  onPressed: () {
                    context.read(autoDisposeCounter.notifier).state++;
                  },
                ),
                Button(
                  label: 'toggle',
                  onPressed: () {
                    setState(() {
                      showCounter = !showCounter;
                    });
                  },
                ),
              ]);
            },
          );
        }),
      );

      expect(find.text('0'), findsOneComponent);
      expect(find.text('hidden'), findsNothing);

      // increase counter
      await tester.click(find.tag('button').first);
      await tester.pump();

      expect(find.text('1'), findsOneComponent);
      expect(find.text('hidden'), findsNothing);

      // hide counter
      await tester.click(find.componentWithText(Button, 'toggle'));
      await tester.pump();

      expect(find.text('1'), findsNothing);
      expect(find.text('hidden'), findsOneComponent);

      // show counter
      await tester.click(find.componentWithText(Button, 'toggle'));
      await tester.pump();

      expect(find.text('0'), findsOneComponent);
      expect(find.text('hidden'), findsNothing);
    });
  });
}
